<?php

/**
 * 设置或获取配置服务
 * @param  string $key  需要获取或者设置的key
 * @param  mixed $value 需要设置的值
 * @return mixed
 */
function config($key, $value = null)
{
    global $config;
    if ($key && $value) {
        if (is_array($value)) {
            foreach ($value as $k => $v)
                $config[$key][$k] = $v;
        }
    }
    return $config[$key];
}


function getclosetext($code){
   $closemap=array(
       6=>'外部指令关闭',
       7=>'心跳超时关闭',
       5=>'旧重复链接关闭'
   );
   return $closemap[$code] ?? '未知原因';
}

function gettbinfo($tbid,$theRedis){
    $tbBaseinfo= $theRedis->hGet("TB_".$tbid, 'baseinfo');
    return json_decode($tbBaseinfo,true);

}

/**
 * 返回ascii码对应的字符
 * @param  array   $code  ascii码数组
 * @return string  assic  码对应的字符串
 */
function hex2ascii(array $code)
{
    if (!is_array($code) && empty($code)) return false;
    $str = null;
    foreach ($code as $row) {
        $str .= chr(hexdec($row));
    }
    return $str;
}

/**
 * 字符串转成ascii码
 * @param $str
 * @return string
 */
function str2ascii($str)
{
    $hex = "";
    $str = str_split($str, 1);
    foreach ($str as $val) {
        $hex .= sprintf("%02x", ord($val));
    }
    return $hex;
}

/**
 * int类型rssi信号转化成负数
 * @param  int    $num rssi信号值
 * @return int         rssi信号值
 */
function rssi(int $num)
{
    return ~($num ^ 255);
}


/**
 * 返回高低字节对应的数字
 * @param  int   $num 需要转换的数组 在255范围内
 * @return array 第一个索引是高字,第二个索引低字
 */
function bitout($num) {
    $high = sprintf("%d", ($num >> 4));
    $low = sprintf("%d", ($num & 240) ^ $num);
    return array($high, $low);
}


/**
 * 格式化MAC地址
 * @param $str
 * @return string
 */
function formatMAC($mac, $separator = ':')
{
    return implode($separator, str_split($mac, 2));
}

/**
 * 获取主次版本
 * @param $hex
 * @return array
 */
function getVer($hex)
{
    $master = $hex >> 4;
    $slave = ($master << 4) ^ $hex;
    return array('master' => $master, 'slave' => $slave);
}

/**
 * 取后8位作为消息和
 * @param $str 16进制数
 * @return string
 */
function getBitSum($num) {
    $one = $num >> 8;
    $two = $one << 8;
    $three = $num ^ $two;
    return sprintf("%02x", $three);
}

/**
 * 计算16进制消息和
 * @param $str
 * @return int|number
 */
function hexSum($str) {
    $sum = 0;
    $str = str_split($str, 2);
    foreach ($str as $r) $sum += hexdec($r);
    return $sum;
}
function hexCalcSum($str) {
    $sum = 0;
    $str = str_split($str, 2);
    foreach ($str as $r) {
        $sum += hexdec($r);
    }
    return $sum;
}

function sumVerify($sum) {
    return sprintf("%04x",$sum);
}




/**
 * getDistbyRssi 计算RSSI的标准距离
 * @param $SdRssi   实际值
 * @param $baserssi 1m参照A值
 * @param $n        衰减系数
 * @return string
 * Created by: xiebaoxin
 */
function  getDistbyRssi($SdRssi,$baserssi,$n){
    $k=(abs($SdRssi)> abs($baserssi)) ? abs($SdRssi)-abs($baserssi) : abs($baserssi)-abs($SdRssi);
    $dis = pow(10,$k/(10*$n) );
    return sprintf("%.3f", $dis);
}


function echoMsg($msg,$redis=null,$filelog=false,$chanel="system_echo",$type='data'){

//    static $monologServer;
    try{
        if($redis && !empty($chanel))
            $redis->publish($chanel, $msg);

        if($filelog)
        {
            $logfile= dirname(dirname(__FILE__)). '/log/'.$type.'log.log';
            file_put_contents($logfile,$msg,FILE_APPEND|LOCK_EX);
        }

//        $monologServer[$type] =new \Core\Monolog($type);
//        $monologServer[$type]($msg);

    } catch (\RedisException $e) {
        echo " redis is off!now try connecting…… \n";
        if($redis)
            \TBServer\Model\TbRedis:: int($redis->workerid);
    }
    catch (Exception $e){echo $e->getMessage();}

}

function vardumpMsg($msg,$redis,$chanel="system_vardump"){
    //   var_dump($msg) ;
    $redis->publish($chanel, json_encode($msg));
}


//---------------独尊热点列表专用函数----------------------------

function getCountHotPoint($httb){
    return count($httb);
}

function getArrHotPoint($httb){
    return array_keys($httb);
}

function getInfoHotPoint($tbid,$httb){
    if(empty($tbid) || empty($httb)) return false;
    return $httb[$tbid];
}


function existHotPoint($tbid,$httb){
    if(empty($tbid) || empty($httb)) return false;
    return array_key_exists($tbid, $httb) ;
}

function  updateHotPoint($tbid,$httb,$data){
    if(empty($tbid) || empty($httb)) return false;

    $rssiarr=$httb[$tbid]['zgarr'];
    $rssiarr= pushinTbBssiIntoHotPiont($rssiarr,abs($data['sendRssi']));

    if(count($rssiarr)>8){
        // $lastrssi=popoutFirstTbBssiHotPiont($rssiarr);//上一个
        // $nowrssi=abs($data['sendRssi']); //当前
        //   $data['sendRssi']=0.618*($lastrssi+$nowrssi)/2+0.372*$data['sendRssi'];

        ////醉鬼滤波 计算当前估计s值
        //  $data['sendRssi']=getdzzgfilter($rssiarr);

        $rssiarr=popoutTbBssiIntoHotPiont($rssiarr);
    }

    $httb[$tbid]=['tbid' => $tbid, 'Rssi' => abs($data['sendRssi']),'dist'=>$data['dist'], 'time' => $data['time'],'zgarr'=>$rssiarr];

    return $httb;
}
function newHotPoint($tbid,$ht,$data){
    if(empty($tbid) || empty($data)) return false;
    if(!isset($data['time'])) $data['time']=time();

    $ht[$tbid]= array('tbid' => $tbid, 'Rssi' => abs($data['sendRssi']),'dist'=>$data['dist'],'time' => $data['time'],'zgarr'=>[abs($data['sendRssi'])] );

    return $ht;
}

function  delHotPoint($tbid,$httb){
    if(empty($tbid) || empty($httb)) return false;
    $objht=$httb;
    unset($objht[$tbid]);
    return $objht;
}


function  delAndUpdateHotPoint($otbid,$ntbid,$httb,$data){
    if(empty($otbid) || empty($httb)) return false;
    unset($httb[$otbid]);
    $httb= newHotPoint($ntbid,$httb,$data);
    return $httb;
}

function  getTbidFromHotPoint($httb,$wh='Rssi',$odr='max'){
    if(empty($wh) || empty($httb)) return 0;
    $objdt= $odr=='max' ? 0: time();
    foreach ($httb as $k=>$v){
        $k=$v['tbid'];
        if($odr=='max'){
            if($v[$wh]>=$objdt) {
                $objdt = $v[$wh];
                $key=$k;
            }
        }
        else{
            if($v[$wh]<=$objdt) {
                $objdt = $v[$wh];
                $key=$k;
            }
        }
    }
    return !empty($key) ? $key :0;
}


function sortItemHotPoint($array,$keys='Rssi',$type='SORT_ASC'){
//$array为要排序的数组,$keys为要用来排序的键名,$type默认为升序排序
    if(!isset($array) || empty($array) ) return false;
    $keysvalue = $new_array = array();
    foreach ($array as $k=>$v){
        $keysvalue[$k] = $v[$keys];
    }
    if($type == 'SORT_ASC'){
        asort($keysvalue);
    }else{
        arsort($keysvalue);
    }
    reset($keysvalue);
    foreach ($keysvalue as $k=>$v){
        $new_array[$k] = $array[$k];
    }
    return $new_array;
}

function sortbyRssiHotPoint_old($ht,$field='Rssi',$order='SORT_ASC'){
    if(!isset($ht) || empty($ht) ) return false;
    //排序

    $sort = array(
        'direction' => $order, //排序顺序标志 SORT_DESC 降序；SORT_ASC 升序
        'field'     => $field,       //排序字段
    );
    $arrSort = array();
    foreach($ht AS $uniqid => $row){
        foreach($row AS $key=>$value){
            $arrSort[$key][$uniqid] = $value;
        }
    }
    if($order){
        array_multisort($arrSort[$field], constant($order), $ht);
    }
    /* 有个弊端,如果你的数组如果是字符串键名将被保留,但是数字键将被重新索引,从0开始,并以1递增。*/
    // var_dump($ht);    //按照大小排序
    return $ht;
}

function pushinTbBssiIntoHotPiont($objarr,$data){
    //$objarr=array("WEB");//声明一个数组当做队列
    array_push($objarr,$data);//将字符串压入栈（数组）中
    return $objarr;
}

function popoutTbBssiIntoHotPiont($objarr){
    // $objarr=array("WEB","www.chhua.com","WEB开发笔记","PHP","站点建设");//声明一个数组当做栈
    array_shift($objarr);//将字符串出队（数组）中
    return $objarr;
}


function popoutFirstTbBssiHotPiont($objarr){
    $max= array_pop($objarr);//数组的最后一个元素
    return $max;
}

function array_remove($data, $key){
    if(!array_key_exists($key, $data)){
        return $data;
    }
    $keys = array_keys($data);
    $index = array_search($key, $keys);
    if($index !== FALSE){
        array_splice($data, $index, 1);
    }
    return $data;

}


/**
 * 独尊醉鬼369滤波
 * @param $data
 * @return bool|float|int
 * Created by: xiebaoxin
 */
function getdzzgfilter($data){
    if(empty($data) ) return false;
    sort($data);

    $n=count($data);
    $sum=array_sum($data);

    $max= array_pop($data);//删除数组的最后一个元素 -最大
    $min= array_shift($data);//删除数组中首个元素，并返回被删除元素的值。-最小
    $cha=$max-$min;////最大偏差
    $tot=0;//最大偏差的差距和
    foreach ($data as $d){
        $tot += ($d-$cha) ;
    }
    $tot+=($max-$cha)+($min-$cha);
    $cha= $tot/$n+$cha;

    return sprintf("%.2f", $cha);
}

function dzfiltertest($data){
    if(empty($data) ) return false;
    sort($data);

    $max= array_pop($data);//删除数组的最后一个元素 -最大
    $min= array_shift($data);//删除数组中首个元素，并返回被删除元素的值。-最小




    $cha= array_sum($data)/count($data);

    return sprintf("%.2f", $cha);
}



/**
 * 根据信号强度计算发射距离
 * 这是原始的噪点数据----需要进行karlman滤波矫正--
 * getDistancefromSdssi
 * @param $SdRssi
 * @param $rssi
 * @param int $n
 * @return float|int
 * Created by: xiebaoxin
 */
function getDistancefromSdssi($data,$n=3){
    //$n=!empty($data['rssi_n'])? $data['rssi_n'] :$n;
    $SdRssi=$data['realRssi'];
    $rssi=$data['rssi_a'];

    if($SdRssi<$rssi)
        return 1.00;
    elseif ($SdRssi<48)
        $n=1;
    else
    {
        $n= (1.11/30) *$SdRssi -0.7; //室外实际测量拟合
        $n=$n*$data['rssi_n'];
        $n=sprintf("%.3f", $n);
    }

    return getDistbyRssi($SdRssi,$rssi,$n);

}

/*
当圆心距等于两圆半径之差时 两圆内切
当圆心距等于两圆半径之和时 两圆外切
当圆心距大于两圆半径之和时 两圆外离
*/
/**
 * 是否相交ifCross2Point
 * 当圆心距小于两圆半径之和 大于半径之差时 两圆相交 z=0
 * @param $objCircleA
 * @param $objCircleB
 * @return bool
 * Created by: xiebaoxin
 */
function ifCross2Point($objCircleA,$objCircleB){
    $d= sqrt(pow(($objCircleA['x']-$objCircleB['x']),2)+pow(($objCircleA['y']-$objCircleB['y']),2));//+pow(($objCircleA['z']-$objCircleB['z']),2)
    return $d<($objCircleA['dist']+$objCircleB['dist'])  && $d > abs($objCircleA['dist']-$objCircleB['dist'])  ? true:false;
}

/**
 * 是否包含ifHavein2Point
 * 当圆心距小于两圆半径之差时 两圆内含 z=0
 * @param $objCircleA
 * @param $objCircleB
 * @return bool
 * Created by: xiebaoxin
 */
function ifHavein2Point($objCircleA,$objCircleB){
    $r= sqrt(pow(($objCircleA['x']-$objCircleB['x']),2)+pow(($objCircleA['y']-$objCircleB['y']),2));//+pow(($objCircleA['z']-$objCircleB['z']),2)
    return abs($objCircleA['dist']-$objCircleB['dist']) > $r ? true:false;
}

function calculateLocation($hotpiontParam){
    $loctiondata = array();
    if (!empty($hotpiontParam)) {

        $oct=getCountHotPoint($hotpiontParam);
        if($oct< 3) return false;

        $i=0;
        $pointarr=array();
        foreach($hotpiontParam as $k=>$vl){
            if($i==0) $pointarr['tb1']=$vl;
            elseif($i==1) $pointarr['tb2']=$vl;
            elseif($i==2) $pointarr['tb3']=$vl;
            else
                break;
            $i++;
        }

        /*  $sht['178'] =['id'=>178,'x'=>0,'y'=>0,'z'=>0, 'dist'=>1,'type'=>'rf','Rssi'=>45];
          $sht['180'] =['id'=>180,'x'=>0,'y'=>1,'z'=>0, 'dist'=>2,'type'=>'rf','Rssi'=>41];
          $sht['190'] =['id'=>190,'x'=>1,'y'=>1,'z'=>0, 'dist'=>3,'type'=>'rf','Rssi'=>56];
  */
        $trilateration = new \TBServer\Helper\Trilateration_xbx($pointarr['tb1'],$pointarr['tb2'], $pointarr['tb3']);
        $point = $trilateration->getIntersectionPoint();

        if($point['triPtX']=="NaN" || empty($point) )
        {
            if(ifCross2Point($pointarr['tb1'],$pointarr['tb2']) &&  ifCross2Point($pointarr['tb1'],$pointarr['tb3']) && ifCross2Point($pointarr['tb2'],$pointarr['tb3']))
                $point = $trilateration->getPoint2Circle(); //两两相交没有公共区域的情况下
            else{
                /*
                    <45 1,就是这个点
                   其中两个相交则曲其中靠近第三个原的一个点
                   否则 就去三原中心
                   */
                if($pointarr['tb1']['Rssi']<45)
                    $point=['triPtX'=>"NaN",'triPtY'=>"NaN",'triPtZ'=>"NaN",'triPtO'=>$pointarr['tb1']['id']];
                elseif(ifCross2Point($pointarr['tb1'],$pointarr['tb2']))
                    $point=$trilateration->getPoint2CircleCross("A","B","C");
                elseif(ifCross2Point($pointarr['tb1'],$pointarr['tb3']))
                    $point=$trilateration->getPoint2CircleCross("A","C","B");
                elseif(ifCross2Point($pointarr['tb2'],$pointarr['tb3']))
                    $point=$trilateration->getPoint2CircleCross("B","C","A");
                else
                    $point = $trilateration->get3PointCenter();

            }

        }


        //   var_dump($point);
        if(!empty($point)){
            $loctiondata['x'] = $point['triPtX'];
            $loctiondata['y'] = $point['triPtY'];
            $loctiondata['z'] = $point['triPtZ'];
            $loctiondata['time']= time();
        }

    }

    return  $loctiondata;


}


/**
 * 计算两点地理坐标之间的距离
 * @param  Decimal $longitude1 起点经度
 * @param  Decimal $latitude1  起点纬度
 * @param  Decimal $longitude2 终点经度
 * @param  Decimal $latitude2  终点纬度
 * @param  Int     $unit       单位 1:米 2:公里
 * @param  Int     $decimal    精度 保留小数位数
 * @return Decimal
 */
function getDistance($longitude1, $latitude1, $longitude2, $latitude2, $unit=2, $decimal=2){

    $EARTH_RADIUS = 6370.996; // 地球半径系数
    $PI = 3.1415926;

    $radLat1 = $latitude1 * $PI / 180.0;
    $radLat2 = $latitude2 * $PI / 180.0;

    $radLng1 = $longitude1 * $PI / 180.0;
    $radLng2 = $longitude2 * $PI /180.0;

    $a = $radLat1 - $radLat2;
    $b = $radLng1 - $radLng2;

    $distance = 2 * asin(sqrt(pow(sin($a/2),2) + cos($radLat1) * cos($radLat2) * pow(sin($b/2),2)));
    $distance = $distance * $EARTH_RADIUS * 1000;

    if($unit==2){
        $distance = $distance / 1000;
    }

    return round($distance, $decimal);

}


//---------------独尊热点列表专用函数--end--------------------------

function handleFatal($serv)
{
    $error = error_get_last();
    if (isset($error['type']))
    {
        switch ($error['type'])
        {
            case E_ERROR :
            case E_PARSE :
            case E_CORE_ERROR :
            case E_COMPILE_ERROR :
                $message = $error['message'];
                $file = $error['file'];
                $line = $error['line'];
                $log = "$message ($file:$line)\nStack trace:\n";
                $trace = debug_backtrace();
                foreach ($trace as $i => $t)
                {
                    if (!isset($t['file']))
                    {
                        $t['file'] = 'unknown';
                    }
                    if (!isset($t['line']))
                    {
                        $t['line'] = 0;
                    }
                    if (!isset($t['function']))
                    {
                        $t['function'] = 'unknown';
                    }
                    $log .= "#$i {$t['file']}({$t['line']}): ";
                    if (isset($t['object']) and is_object($t['object']))
                    {
                        $log .= get_class($t['object']) . '->';
                    }
                    $log .= "{$t['function']}()\n";
                }
                if (isset($_SERVER['REQUEST_URI']))
                {
                    $log .= '[QUERY] ' . $_SERVER['REQUEST_URI'];
                }
                error_log($log);
                $serv->reload();
            // $serv->send($this->currentFd, $log);
            default:
                break;
        }
    }
}



function creatLogFile($file){
    if(file_exists($file)){
        if(is_writable($file)){
            return $file;
        }else{
            return false;
        }
    }else{
        $res = fopen($file,'a+');
        if($res){
            fclose($res);
            return $file;
        }else{
            return false;
        }
    }
}

/**
 * 获取当前redis的数据库编号
 * @return mixed
 */
function getDbNum()
{
	$redis_config = config('redis');

	return $redis_config['db'];
}

/**
 * 获取放到redis中的实时数据列表的键值
 * 当键值不存在时返回空
 * @param $type
 * @return string
 */
function getRedisTypeKey($type)
{
	static $keyArray = [
		TYPE_STA => 'STA',
		TYPE_AP  => 'AP',
		TYPE_RF  => 'RF'
	];

	return 'IN_TIME_' . ($keyArray[$type] ?? '');
}